package com.zmx.b2c.order.service;

import com.zmx.b2c.order.common.CompletableFutureThreadPool;
import com.zmx.b2c.order.dto.B2COrderDto;
import com.zmx.b2c.order.dto.B2COrderItemDto;
import com.zmx.b2c.order.dto.B2COrderLogDto;
import com.zmx.b2c.order.dto.params.CreateOrderParams;
import com.zmx.b2c.order.dto.params.OrderParamsDTO;
import com.zmx.b2c.order.dto.params.ProductParamsDTO;
import com.zmx.b2c.pay.dto.PaymentDto;
import com.zmx.b2c.pay.dto.PaymentLogDto;
import com.zmx.b2c.pay.service.PaymentLogService;
import com.zmx.b2c.pay.service.PaymentService;
import com.zmx.b2c.stock.dto.ProductStockDto;
import com.zmx.b2c.stock.service.ProductStockService;
import com.zmx.common.common.contents.GenerateCodePrefix;
import com.zmx.common.common.contents.MqQueueContents;
import com.zmx.common.common.contents.ProductStockContents;
import com.zmx.common.common.enums.Enum;
import com.zmx.common.common.exception.BusinessException;
import com.zmx.common.common.invocation.InvocationInfoProxy;
import com.zmx.common.common.utils.Lock.RedissonLock;
import com.zmx.common.common.utils.id.Id;
import com.zmx.product.api.ProductApi;
import com.zmx.product.dto.ProductDto;
import com.zmx.user.api.MemberApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @Description: 创建订单实现类
 * @ClassName: CreateOrderService
 * @Author zhaomx
 * @Version 1.0
 * @Date 2022-01-05 22:23
 */
@Slf4j
@Component
public class ToCreateOrderService {
    @Autowired
    private Id id;
    @Autowired
    private RedissonLock redissonLock;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductApi productApi;
    @Autowired
    private MemberApi memberApi;
    @Autowired
    private B2COrderItemService b2COrderItemService;
    @Autowired
    private B2COrderLogService b2COrderLogService;
    @Autowired
    private B2COrderService b2COrderService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private PaymentLogService paymentLogService;
    @Autowired
    private ToCreateOrderService toCreateOrderService;
    @Autowired
    private ProductStockService productStockService;

    /**
     * 构建订单参数
     * @param createOrderParams
     * @return
     */
    public OrderParamsDTO createOrderParams(CreateOrderParams createOrderParams) {
        log.info("begin to create orderParams {{}}" + createOrderParams.toString());

        List<Long> productIds = createOrderParams.getProductParamsDTOs().stream().map(ProductParamsDTO::getProductId).collect(Collectors.toList());
        //多线程查询商品及商品库存，加快处理速度
        CompletableFutureThreadPool completableFutureThreadPool = new CompletableFutureThreadPool();
        ThreadPoolExecutor pool = completableFutureThreadPool.poolExecutor();
        List<CompletableFuture<?>> futureList = new ArrayList<CompletableFuture<?>>(3);

        CompletableFuture<List<ProductDto>> productDtosfuture = CompletableFuture.supplyAsync(() -> {
            List<ProductDto> productDtos = productApi.findByIdIn(productIds.toArray(new Long[0]));
            return productDtos;
        }, pool);
        futureList.add(productDtosfuture);
        CompletableFuture<List<ProductStockDto>> productStockDtosfuture = CompletableFuture.supplyAsync(() -> {
            List<ProductStockDto> productStockDtos = productStockService.getByProductIdIn(productIds.toArray(new Long[0]));
            return productStockDtos;
        }, pool);
        futureList.add(productStockDtosfuture);
        //CompletableFuture<MemberDto> memberDtofuture = CompletableFuture.supplyAsync(() -> {
        //    MemberDto memberDTO = memberApi.findByOpenid(createOrderParams.getOpenId());
        //    return memberDTO;
        //}, pool);
        //futureList.add(memberDtofuture);

        //所有任务都执行完成并返回
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()])).join();

        List<ProductDto> productDtos = productDtosfuture.join();
        List<ProductStockDto> productStockDtos = productStockDtosfuture.join();
        //MemberDto memberDTO = memberDtofuture.join();

        Long memberId = Long.valueOf(InvocationInfoProxy.getUserId());
        //构建订单实体
        B2COrderDto orderDTO = new B2COrderDto(null, memberId, "", Enum.OrderStatus.confirmed.getCode(), Enum.PaymentStatus.unpaid.getCode(), Enum.DeliveryStatus.undelivery.getCode(), createOrderParams.getPlatform(), createOrderParams.getOrderSource(), createOrderParams.getConsignee(), createOrderParams.getPhone(), createOrderParams.getAddress(), createOrderParams.getArea(), createOrderParams.getAmountPaid(), createOrderParams.getFreight(), createOrderParams.getTotalPrice(), createOrderParams.getLeaveWord());

        //构建订单详情实体
        List<B2COrderItemDto> orderItemDTOs = new ArrayList<B2COrderItemDto>();
        for (ProductParamsDTO productParamsDTO : createOrderParams.getProductParamsDTOs()) {
            //根据productId获取商品信息
            List<ProductDto> productDtoList = productDtos.stream().filter(productDto -> {
                return Objects.equals(productDto.getId(), productParamsDTO.getProductId());
            }).collect(Collectors.toList());
            ProductDto productDto = productDtoList.get(0);

            List<ProductStockDto> productStockDtoList = productStockDtos.stream().filter(productStockDto -> {
                return Objects.equals(productStockDto.getProductId(), productParamsDTO.getProductId());
            }).collect(Collectors.toList());
            ProductStockDto productStockDto = productStockDtoList.get(0);

            //判断库存是否足够,正常下单逻辑只是简单校验库存是否足够，不必加锁，支付时再去扣减库存
            int quantity = productParamsDTO.getQuantity();
            checkStock(quantity, productStockDto, productDto);

            B2COrderItemDto orderItemDTO = new B2COrderItemDto(productParamsDTO.getActiveId(), productDto.getId(), productDto.getPrice(), productDto.getName(), quantity);
            orderItemDTOs.add(orderItemDTO);
        }

        OrderParamsDTO orderParamsDTO = new OrderParamsDTO(orderDTO, orderItemDTOs);
        return orderParamsDTO;
    }

    /**
     * 检查库存
     * @param quantity
     * @param productDto
     */
    private void checkStock(int quantity, ProductStockDto productStockDto, ProductDto productDto) {
        if (quantity > productStockDto.getAvailableStock()) {
            throw new BusinessException("商品[" + productDto.getName() + "]库存不足");
        }
    }

    /**
     * 秒杀检查库存，先从redis缓存查，如果没有再去数据库查
     * @param quantity
     * @param productDTO
     */
    private void checkStockSkill(int quantity, ProductDto productDTO) {
        int stock = 0;
        Object object = redisTemplate.opsForValue().get(ProductStockContents.PRODUCT_STOCK_NUM_KEY_PREFIX + productDTO.getId());
        if (object == null) {
            ProductStockDto productStockDto = productStockService.getByProductId(productDTO.getId());
            if (productStockDto != null) {
                stock = productStockDto.getAvailableStock();
            }
        } else {
            stock = Integer.valueOf(object.toString());
        }

        if (quantity > stock) {
            throw new BusinessException("商品[" + productDTO.getName() + "]库存不足");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public OrderParamsDTO toCreateOrder(OrderParamsDTO orderParamsDTO) {
        B2COrderDto orderDTO = orderParamsDTO.getB2COrderDto();
        List<B2COrderItemDto> orderItemDTOs = orderParamsDTO.getB2COrderItemDtos();

        //创建订单
        B2COrderDto b2COrderDto = new B2COrderDto();
        b2COrderDto.setMemberId(orderDTO.getMemberId());
        b2COrderDto.setOrderSn(id.generateCode(GenerateCodePrefix.ORDER));
        b2COrderDto.setOrderStatus(Enum.OrderStatus.confirmed.getCode());
        b2COrderDto.setPaymentStatus(Enum.PaymentStatus.unpaid.getCode());
        b2COrderDto.setDeliveryStatus(Enum.DeliveryStatus.undelivery.ordinal());
        b2COrderDto.setPlatform(orderDTO.getPlatform());
        b2COrderDto.setOrderSource(orderDTO.getOrderSource());
        b2COrderDto.setConsignee(orderDTO.getConsignee());
        b2COrderDto.setPhone(orderDTO.getPhone());
        b2COrderDto.setAddress(orderDTO.getAddress());
        b2COrderDto.setArea(orderDTO.getArea());
        b2COrderDto.setAmountPaid(orderDTO.getAmountPaid());
        b2COrderDto.setFreight(orderDTO.getFreight());
        b2COrderDto.setTotalPrice(orderDTO.getTotalPrice());
        b2COrderDto.setLeaveWord(orderDTO.getLeaveWord());
        B2COrderDto orderDto = b2COrderService.create(b2COrderDto);
        log.info("create order success  orderId: {}, orderSn: {}" + orderDto.getId(), orderDto.getOrderSn());

        for (B2COrderItemDto orderItemDTO : orderItemDTOs) {
            //创建订单详情
            B2COrderItemDto b2COrderItemDto = new B2COrderItemDto();
            b2COrderItemDto.setOrderId(orderDto.getId());
            b2COrderItemDto.setActiveId(orderItemDTO.getActiveId());
            b2COrderItemDto.setProductId(orderItemDTO.getProductId());
            b2COrderItemDto.setPrice(orderItemDTO.getPrice());
            b2COrderItemDto.setName(orderItemDTO.getName());
            b2COrderItemDto.setQuantity(orderItemDTO.getQuantity());
            b2COrderItemService.create(b2COrderItemDto);
        }
        log.info("create orderItem success");

        //创建订单日志,异步执行
        B2COrderLogDto b2COrderLogDto = new B2COrderLogDto();
        b2COrderLogDto.setContent("create order by orderService");
        b2COrderLogDto.setType(0);
        b2COrderLogDto.setOrders(orderDto.getId());
        toCreateOrderService.createB2COrderLogDto(b2COrderLogDto);
        log.info("create orderLog success");

        orderParamsDTO.setOrderSn(orderDto.getOrderSn());
        orderParamsDTO.setB2COrderDto(orderDTO);
        log.info("create order orderItem orderLog all success!");
        return orderParamsDTO;
    }

    /**
     * 记录支付单（预先记录，真正支付成功后回写支付表）
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void toCreatePayment(OrderParamsDTO orderParamsDTO) {
        B2COrderDto b2COrderDto = orderParamsDTO.getB2COrderDto();
        //写入支付表
        PaymentDto paymentDto = new PaymentDto();
        paymentDto.setAmount(b2COrderDto.getTotalPrice());
        paymentDto.setFee(BigDecimal.ZERO);
        paymentDto.setMethod(1);
        paymentDto.setPayer(b2COrderDto.getConsignee());
        paymentDto.setPaymentPluginId("wxPayPlugin");
        paymentDto.setSn(id.generateCode(GenerateCodePrefix.PAY));
        paymentDto.setStatus(Enum.PaymentStatus.unpaid.ordinal());
        paymentDto.setMemberId(b2COrderDto.getMemberId());
        paymentDto.setOrders(b2COrderDto.getId());
        paymentDto.setPayType(0);
        paymentDto.setIsEnabled(1);
        paymentDto.setPlatform(0);
        PaymentDto payment = paymentService.create(paymentDto);

        //写入日志表
        PaymentLogDto paymentLogDto = new PaymentLogDto();
        paymentLogDto.setPaymentSn(payment.getSn());
        paymentLogDto.setSteps(1);
        paymentLogDto.setReqparams("暂无");
        paymentLogDto.setOrderSn(b2COrderDto.getOrderSn());
        paymentLogDto.setMemberId(b2COrderDto.getMemberId());
        paymentLogDto.setPlatform(0);
        paymentLogService.create(paymentLogDto);
    }

    /**
     * 超时订单放入死信队列
     *
     * @param orderCode
     */
    @Async
    public void sendTimeOutOrder(String orderCode) {
        amqpTemplate.convertAndSend(MqQueueContents.ORDER_TIMEOUT_QUEUE, MqQueueContents.ORDER_TIMEOUT_NOPAY_KEY, orderCode);
    }

    /**
     * 记录订单日志
     */
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void createB2COrderLogDto(B2COrderLogDto b2COrderLogDto) {
        //记录取消订单日志
        b2COrderLogService.create(b2COrderLogDto);
    }
}
